package com.joysuch.wwyt.workflow.service.impl;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.common.util.MapUtils;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.risk.entity.RiskDistrict;
import com.joysuch.wwyt.risk.entity.RiskDynamicConfig;
import com.joysuch.wwyt.risk.entity.RiskDynamicRecord;
import com.joysuch.wwyt.risk.entity.RiskEvaluationControl;
import com.joysuch.wwyt.risk.repository.RiskDistrictDao;
import com.joysuch.wwyt.risk.repository.RiskDynamicConfigDao;
import com.joysuch.wwyt.risk.repository.RiskEvaluationControlDao;
import com.joysuch.wwyt.risk.service.RiskDynamicRecordService;
import com.joysuch.wwyt.risk.service.RiskEvaluationBusinessService;
import com.joysuch.wwyt.workflow.entity.WorkFlowLocationConfig;
import com.joysuch.wwyt.workflow.repository.WorkFlowLocationConfigDao;
import com.joysuch.wwyt.workflow.service.WorkFlowTaskDynamicService;
import com.joysuch.wwyt.workflow.service.WorkFlowTaskProcessListService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.redisson.api.RList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author ZhaoQiuWei
 * @Date 2022/4/26 14:54
 */
@Service
@Slf4j
public class WorkFlowTaskDynamicServiceImpl implements WorkFlowTaskDynamicService {

    @Autowired
    private RiskDynamicRecordService riskDynamicRecordService;

    @Autowired
    private RiskEvaluationBusinessService riskEvaluationBusinessService;

    @Autowired
    private RiskEvaluationControlDao riskEvaluationControlDao;

    @Autowired
    private RiskDynamicConfigDao riskDynamicConfigDao;

    @Autowired
    private WorkFlowTaskProcessListService workFlowTaskProcessListService;

    @Autowired
    private WorkFlowLocationConfigDao workFlowLocationConfigDao;

    @Autowired
    private RiskDistrictDao riskDistrictDao;

    @Autowired
    private RedisClient redisClient;


    //与雪晨沟通 目前只有作业票根据点位范围影响区域的动态等级
    @Override
    public void saveDynamicRecords(Long processId, String formJson, String taskCode, String currentTaskNode, String formData, boolean nextTaskFlag) {
        List<RiskDynamicRecord> batchRecord = new ArrayList<>();
        Date date = new Date();
        Long userId = Context.getCurrentUserId();
        RiskEvaluationControl dyCont = riskEvaluationControlDao.findByNameAndOrgCodeAndTenentId("动态安全风险评估", Context.getCompanyCode(), Context.getCurrentTenantId());
        log.info("作业票{} 动态风险-是否有下一个节点{},动态风险开关{}", nextTaskFlag, dyCont.getSwitchState().intValue() == 1 ? "开启" : "关闭");
        //当前是最后一个节点时不参与动态风险的计算 || 动态开关 关闭时不参与动态风险计算
        if (nextTaskFlag && dyCont.getSwitchState().intValue() == 1) {
            //获取作业票关联的区域
            List<RiskDistrict> eligibleDistricts = getEligibleDistricts(processId, formData, taskCode);
            log.info("作业票{} 动态风险-本次有交点的区域:{}", taskCode, JSON.toJSONString(eligibleDistricts));
            //获取作业票关联的动态配置
            RiskDynamicConfig config = getEligibleDynamicConfigs(processId, formJson, formData, currentTaskNode);
            log.info("作业票{} 动态风险-本次参与的动态配置:{}", taskCode, JSON.toJSONString(config));
            //保存该作业票关联的区域和动态配置信
            if (!CollectionUtils.isEmpty(eligibleDistricts) && config != null) {
                for (RiskDistrict district : eligibleDistricts) {
                    RiskDynamicRecord record = new RiskDynamicRecord();
                    record.setId(record.getId());
                    record.setDynamicConfigId(config.getId());
                    record.setRiskDistrictId(district.getId());
                    record.setRiskType(1);
                    record.setTaskCode(taskCode);
                    record.setTaskNode(currentTaskNode);
                    record.setDynamicConfigId(config.getId());
                    record.setScore(config.getScore());
                    record.setStatus(0);
                    record.setOrgCode(Context.getCompanyCode().substring(0, 7));
                    record.setTenentId(Context.getCurrentTenantId());
                    record.setCreateTime(date);
                    record.setCreateBy(userId);
                    record.setUpdateTime(date);
                    record.setUpdateBy(userId);
                    batchRecord.add(record);
                }
            }
        }
        if (CollectionUtils.isEmpty(batchRecord)) {
            //如果作业票本次没有参与动态计算 找出redis中最后一个该作业票动态记录id 状态改为不参与
            List<RiskDynamicRecord> oldRecords = riskDynamicRecordService.findAllByTaskCodeRiskTypeAndStatus(taskCode, 1, 0, Context.getCompanyCode().substring(0, 7), Context.getCurrentTenantId());
            if (!CollectionUtils.isEmpty(oldRecords)) {
                oldRecords.forEach(old -> {
                    old.setStatus(1);
                });
                batchRecord.addAll(oldRecords);
            }
        }
        if (!CollectionUtils.isEmpty(batchRecord)) {
            saveRedisDynamicData(JSONUtil.toJsonStr(batchRecord));
        }
    }

    private String getRedisDynamicKey(String companyCode, Long tenentId) {
        return companyCode + ":" + tenentId + ":" + "dynTask";
    }

    private void saveRedisDynamicData(String redisRecord) {
        String orgCode = Context.getCompanyCode().substring(0, 7);
        Long tenentId = Context.getCurrentTenantId();
        String key = getRedisDynamicKey(orgCode, tenentId);
        RList<String> list = redisClient.getList(key);
        if (cn.zlg.common.util.CollectionUtils.isEmpty(list)) {
            list.add(redisRecord);
        } else {
            list.add(redisRecord);
        }
    }


    @Override
    public void batchDeletaDynamicRecordsByTaskCode(String code) {
        List<RiskDynamicRecord> records = new ArrayList<>();
        List<RiskDynamicRecord> recordList = riskDynamicRecordService.getAllByTaskCodeAndRiskType(code, 1);
        if (!cn.zlg.common.util.CollectionUtils.isEmpty(recordList)) {
            List<Long> deleteIds = records.stream().map(RiskDynamicRecord::getId).collect(Collectors.toList());
            log.info("作业票{} 动态风险-本次删除的数据库动态记录id集合:{}", code, JSON.toJSONString(deleteIds));
            records = recordList;
        }
        //获取redis中该作业票动态风险缓存的记录并更新
        String key = getRedisDynamicKey(Context.getCompanyCode().substring(0, 7), Context.getCurrentTenantId());
        List<String> redisValue = redisClient.getList(key);
        if (!CollectionUtils.isEmpty(redisValue)) {
            Iterator<String> iterator = redisValue.iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                List<RiskDynamicRecord> redisRecords = JSONUtil.toList(next, RiskDynamicRecord.class);
                if (!CollectionUtils.isEmpty(redisRecords) && redisRecords.get(0).getTaskCode().equals(code)) {
                    records.addAll(redisRecords);
                    iterator.remove();
                }
            }
        }
        if (!cn.zlg.common.util.CollectionUtils.isEmpty(records)) {
            for (RiskDynamicRecord record : records) {
                record.setStatus(1);
                record.setUpdateTime(new Date());
                record.setUpdateBy(Context.getCurrentUserId());
            }
            riskDynamicRecordService.saveOrUpdateBatch(records);
            riskEvaluationBusinessService.editDynamicDistrictLevel(records, "作业票");
        }
    }

    private RiskDynamicConfig getEligibleDynamicConfigs(Long processId, String formJson, String formData, String currentTaskNode) {
        //找到该流程下涉及的
        List<RiskDynamicConfig> dynamicConfigs = riskDynamicConfigDao.findByTypeAndEnableFlag(processId, 1, 1);
        List<RiskDynamicConfig> configs = new ArrayList<>();
        List<RiskDynamicConfig> nullValueConfigs = new ArrayList<>();
        //与产品沟通 一个作业票只对应一个配置,且先匹配有组件和参数的
        for (RiskDynamicConfig config : dynamicConfigs) {
            String optionParamName = config.getOptionParamName();
            String node = config.getNode();
            if (StringUtils.isBlank(node)) {
                continue;
            }
            if (StringUtils.isNotBlank(config.getOptionTypeProxyName()) && StringUtils.isNotBlank(optionParamName)) {
                String key = workFlowTaskProcessListService.findProcessFormJsonKey(config.getOptionTypeName(), formJson);
                if (StringUtils.isBlank(key)) {
                    continue;
                }
                String keyValue = workFlowTaskProcessListService.findTaskFromDataValByKey(key, formData);
                if (StringUtils.isBlank(keyValue) || !optionParamName.equals(keyValue)) {
                    continue;
                }
                configs.add(config);
            }

            if (StringUtils.isBlank(config.getOptionTypeProxyName()) && StringUtils.isBlank(optionParamName)) {
                nullValueConfigs.add(config);
            }
        }
        if (!CollectionUtils.isEmpty(configs)) {
            return getEligibleConfig(configs, currentTaskNode);

        }
        if (!CollectionUtils.isEmpty(nullValueConfigs)) {
            return getEligibleConfig(nullValueConfigs, currentTaskNode);
        }
        return null;
    }

    private RiskDynamicConfig getEligibleConfig(List<RiskDynamicConfig> configs, String currentTaskNode) {
        for (RiskDynamicConfig config : configs) {
            List<String> nodeList = JSONArray.parseArray(config.getNode(), String.class);
            if (StringUtils.isNotBlank(currentTaskNode) && nodeList.contains(currentTaskNode)) {
                return config;
            }
        }

        return null;
    }


    private List<RiskDistrict> getEligibleDistricts(Long processId, String formData, String taskCode) {
        List<RiskDistrict> eligibleDistricts = new ArrayList<>();
        WorkFlowLocationConfig locationConfig = workFlowLocationConfigDao.findFirstByProcessId(processId);
        if (locationConfig == null || StringUtils.isBlank(locationConfig.getComponentKeyName())) {
            return eligibleDistricts;
        }
        String mapKey = locationConfig.getComponentKeyName();
        JSONObject parseObject = JSONObject.parseObject(formData);
        //坐标点位
        JSONArray points = null;
        if (mapKey != null) {
            try {
                JSONObject mapJsonObject = parseObject.getJSONObject(mapKey);
                if (mapJsonObject != null) {
                    // 先取圆形区域点位
                    points = mapJsonObject.getJSONArray("circlePoints");
                    // 圆形点位列表空则 取 points
                    if (points == null || points.size() == 0) {
                        points = mapJsonObject.getJSONArray("points");
                    }
                }
            } catch (Exception e) {
                log.error("动态等级-作业票点位解析失败 {}：", parseObject.toJSONString(), e);
            }
        }
        if (cn.zlg.common.util.CollectionUtils.isEmpty(points)) {
            log.error("动态等级作业票{}点位为空 ", taskCode);
            return eligibleDistricts;
        }
        //获取作业票点位
        Coordinate[] taskCoordinates = getTaskCoordinate(points);
        List<RiskDistrict> districtList = riskDistrictDao.findAll();
        //需要考虑作业票等级被修改 动态配置作业票类型 组件类型 参数 节点被修改及区域被修改删除的特殊情况
        if (cn.zlg.common.util.CollectionUtils.isNotEmpty(districtList)) {
            for (RiskDistrict riskDistrict : districtList) {
                if (StringUtils.isBlank(riskDistrict.getCoordinates())) {
                    continue;
                }
                MapUtils.SafetyPoints districtPoints = MapUtils.parse3DSafetyPointsFromJson(riskDistrict.getCoordinates());
                if (districtPoints == null || cn.zlg.common.util.CollectionUtils.isEmpty(districtPoints.getPoints())) {
                    continue;
                }
                //区域点位
                Coordinate[] districtCoordinates = getCoordinate(districtPoints.getPoints());
                try {
                    GeometryFactory gf = new GeometryFactory();
                    Geometry taskGeometry = gf.createPolygon(taskCoordinates);
                    Geometry areaGeometry = gf.createPolygon(districtCoordinates);
                    //放入有公共点的区域
                    if (taskGeometry.intersects(areaGeometry)) {
                        eligibleDistricts.add(riskDistrict);
                    }
                } catch (Exception e) {
                    log.error("作业票" + taskCode + " -动态风险 区域匹配错误: " + e.getMessage(), e);
                    continue;
                }
            }
        }
        return eligibleDistricts;
    }

    @Override
    public Coordinate[] getTaskCoordinate(JSONArray points) {
        Coordinate[] coor = new Coordinate[points.size()];
        for (int i = 0; i < points.size(); i++) {
            BigDecimal xDecimal = (BigDecimal) ((JSONObject) points.get(i)).get("x");
            BigDecimal yDecimal = (BigDecimal) ((JSONObject) points.get(i)).get("y");
            coor[i] = new Coordinate(xDecimal.doubleValue(), yDecimal.doubleValue());
        }
        return coor;
    }

    @Override
    public Coordinate[] getCoordinate(List<MapUtils.SafetyPoint> points) {
        Coordinate[] coor = new Coordinate[points.size()];
        for (int i = 0; i < points.size(); i++) {
            coor[i] = new Coordinate(points.get(i).getX(), points.get(i).getY());
        }
        return coor;
    }


    @Override
    public void processDynamicRecords(RList<String> records, String orgCode, Long tenentId) {
        Iterator<String> iterator = records.iterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            //作业票本批次操作的记录
            List<RiskDynamicRecord> dyRecords = JSONUtil.toList(next, RiskDynamicRecord.class);
            //作业票该批次操作之前 所有的参与记录 全部改为不参与
            String taskCode = dyRecords.get(0).getTaskCode();
            List<RiskDynamicRecord> oldRecords = riskDynamicRecordService.findAllByTaskCodeRiskTypeAndStatus(taskCode, 1, 0, orgCode, tenentId);
            if (!CollectionUtils.isEmpty(oldRecords)) {
                oldRecords.forEach(old -> {
                    old.setStatus(1);
                });
                dyRecords.addAll(oldRecords);
            }
            //保存本批次的操作信息
            riskDynamicRecordService.saveOrUpdateBatch(dyRecords);
            riskEvaluationBusinessService.editDynamicDistrictLevel(dyRecords, "作业票");
            iterator.remove();
        }
    }

}
